package top.easyblog.core.support.queue;

import cn.hutool.core.util.ArrayUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.util.concurrent.TimeUnit;

/**
 * @author: frank.huang
 * @date: 2023-12-30 23:25
 */
@Slf4j
public class RedisBlockingQueue<T> {
    private String queueKey;
    private BoundListOperations<String, T> listOps;
    private int maxQueueSize; // 最大队列长度

    public RedisBlockingQueue(String queueKey, RedisTemplate<String, T> redisTemplate, int maxQueueSize) {
        this.queueKey = queueKey;
        this.listOps = redisTemplate.boundListOps(queueKey);
        this.maxQueueSize = maxQueueSize;
        // 设置key和value的序列化器
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        redisTemplate.afterPropertiesSet();
    }

    public void offer(T item) {
        while (size() >= maxQueueSize) {
            // 队列已满，等待消费者取出元素
            try {
                Thread.sleep(1000); // 可根据实际情况调整等待时间
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        listOps.leftPush(item);
    }

    public T poll() {
        // 从队列右侧取出元素，在这里，参数0表示阻塞时间（单位：毫秒）。
        // 当队列为空时，如果设置阻塞时间为0，则表示立即返回null，而不进行等待。如果设置的阻塞时间大于0，
        // 则表示在指定的时间内等待，直到队列中有可弹出的元素或超时。
        //
        //具体解释如下：
        //
        //如果队列非空，会立即将左侧的一个元素弹出并返回该元素。
        //如果队列为空且阻塞时间为0，那么方法会立即返回null。
        //如果队列为空且阻塞时间大于0，那么方法会在指定的时间内等待，直到队列中有可弹出的元素或超时。如果超时时仍没有可弹出的元素，则方法会返回null。
        //这边的listOps.rightPop(0)被用于实现一个阻塞的队列操作，即当队列为空时，消费者线程会在此处等待，直到队列中有可弹出的元素或超时。
        try {
            return listOps.rightPop(1000, TimeUnit.MICROSECONDS);
        } catch (Exception e) {
            log.info("获取redis队列失败：", e);
        }

        return null;
    }

    /**
     * 批量添加
     *
     * @param items
     */
    public void addAll(T... items) {
        if (ArrayUtil.isEmpty(items)) {
            return;
        }
        long newSize = size() + items.length;
        while (newSize >= maxQueueSize) {
            log.info("压缩码队列达到上限，等到队列空间释放");
            // 队列已满，等待消费者取出元素
            try {
                Thread.sleep(1000); // 可根据实际情况调整等待时间
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        listOps.leftPushAll(items);
    }


    public int size() {
        return Math.toIntExact(listOps.size());
    }

    public boolean isEmpty() {
        return size() == 0;
    }


}
